$.fn.extend({
    jsonColorful : function(to = false){
        let jsonStr = this.text();
        let jsonObj = null;
        if(!jsonStr) jsonStr = this.val();
        if(jsonStr){
            try{
                jsonObj = JSON.parse(jsonStr);
            }catch(e){
                console.warn(e);
            }
            if(jsonObj != null ){
                let tar = to ? to : this;
                tar.html(this._renderJsonColorful(jsonObj))
                this._addClikcEvent(tar);
            }
        }
        return this;
    },
    _addClikcEvent : function(tar){
        tar.find('.json-toggle').on('click', function(){
            let $i = $(this).children('i');
            if($i.hasClass('fa-plus-square-o')){
                $i.addClass('fa-minus-square-o');
                $i.removeClass('fa-plus-square-o');
            }else{
                $i.addClass('fa-plus-square-o');
                $i.removeClass('fa-plus-minus-o');
            }
            $(this).next().next().next().toggle()
        });
    },
    _renderJsonColorful: function(json, deep = 1, key = 1, lenght = 1){
        type = this._getDataRealType(json);
        let prefix = '&ensp;'.repeat(deep*4);
        let suffix = '&ensp;'.repeat((deep-1)*4);
        let i = 1, html = '',data = '';
        let that = this;
        if(type == 'object'){
            html += '{<span class="toggle-container"><br>\n'
            let len = Object.keys(json).length
            $.each(json, function(idx, val){
                let valType = that._getDataRealType(val);
                if($.inArray(valType, ['object', 'array'])!= -1) {
                    html += that._genDataToggleHtml();
                    prefix = '&ensp;'.repeat(deep*4 - 1);
                }
                html += prefix + that._genIdxHtml(idx)
                html += that._renderJsonColorful(val, deep + 1, i, len)
                i += 1;
            });
            html += suffix + '</span>}'
        }else if(type == 'array'){
            html += '[<span class="toggle-container"><br>\n'
            $.each(json, function(idx, val){
                html += prefix
                html += that._renderJsonColorful(val, deep + 1, i, json.length)
                i += 1;
            });
            html += suffix + '</span>]'
        }else{
            html += this._genValHtml(json, type);
        }
        if( key != lenght ){
            html += ',';
        }
        html += '<br>\n';
        return html;
    },
    _getDataRealType: function(dat){
        let str = toString.call(dat);
        let type = str.split(' ')[1];
        type = type.substr(0, type.length - 1).toLowerCase();
        return type;
    },
    _JSON_COLOR_CONST : {
        MINUS : '<i class="fa fa-minus-square-o" aria-hidden="true"></i>',
        PLUS : '<i class="fa fa-plus-square-o" aria-hidden="true"></i>',
        TAB : '&ensp;'
    },
    _genIdxHtml: function(name){
        return '<span class="PropertyName">"' + name + '"</span><span class="colon">:&ensp;</span>';
    },
    _genValHtml: function(name, type){
        let string = "";
        if(type == 'string') string = '"';
        return '<span class="type-' + type + '">' + string + name + string + '</span>';
    },
    _genDataToggleHtml(){
        return ret = '<span class="json-toggle">'+this._JSON_COLOR_CONST.MINUS+'&ensp;</span>';
    }
});
